﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Mirror.CliMetadata.Tables;
using System.Reflection;
using Kokomo.Mirror.CliMetadata.Signatures;

namespace Kokomo.Mirror.CliMetadata
{
	class CliArrayType : CliSyntheticType
	{
		internal CliArrayType(
			CliMemberBasicInfo basicInfo,
			Type elementType,
			int rank = 1
			)
			: base(basicInfo, elementType)
		{
			this._rank = rank;
		}

		protected override System.Reflection.TypeAttributes GetAttributeFlagsImpl()
		{
			return System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Serializable;
		}

		protected override bool IsArrayImpl()
		{
			return true;
		}

		public override string Name
		{
			get
			{
				if (this._rank == 1)
					return this.GetElementType().Name + "[]";
				else
					return this.GetElementType().Name + "[" + new string(',', this._rank - 1) + "]"; ;
			}
		}

		private int _rank;
		public override int GetArrayRank()
		{
			return this._rank;
		}

		public override Type BaseType
		{
			get { return typeof(Array); }
		}

		private ConstructorInfo CreateConstructor1()
		{
			CliParameterInfo[] parameters = new CliParameterInfo[this._rank];
			for (int i = 0; i < this._rank; i++)
			{
				CliParameterInfo parameter1 = new CliParameterInfo(
					basicInfo,
					"num",
					ParameterAttributes.None,
					i,
					Param.Int32Param,
					this.CliModule.CliAssembly.LoadContext.GetType(TypeCode.Int32)
					);
				parameters[i] = parameter1;
			}
			CliSyntheticConstructorInfo constructor = new CliSyntheticConstructorInfo(this.basicInfo.module, this, ".ctor", MethodAttributes.Public, MethodImplAttributes.InternalCall, parameters);
			return constructor;
		}
		private ConstructorInfo CreateConstructor2()
		{
			CliParameterInfo[] parameters = new CliParameterInfo[this._rank * 2];
			for (int i = 0; i < parameters.Length; i++)
			{
				CliParameterInfo parameter1 = new CliParameterInfo(
					basicInfo,
					"num",
					ParameterAttributes.None,
					i + 1,
					Param.Int32Param,
					this.CliModule.CliAssembly.LoadContext.GetType(TypeCode.Int32)
					);
				parameters[i] = parameter1;
			}
			CliSyntheticConstructorInfo constructor = new CliSyntheticConstructorInfo(this.basicInfo.module, this, ".ctor", MethodAttributes.Public, MethodImplAttributes.InternalCall, parameters);
			return constructor;
		}
		private MethodInfo CreateGetAccessorMethod(
			string methodName,
			Type returnType,
			int indexCount
			)
		{
			CliParameterInfo[] parameters = new CliParameterInfo[indexCount];
			CliParameterInfo returnParameter = new CliParameterInfo(
				basicInfo,
				string.Empty,
				ParameterAttributes.None,
				0,
				new Param() { },
				returnType
				);
			for (int i = 0; i < indexCount; i++)
			{
				CliParameterInfo indexParameter = new CliParameterInfo(
					basicInfo,
					"index" + i,
					ParameterAttributes.None,
					i + 1,
					Param.Int32Param,
					this.CliModule.CliAssembly.LoadContext.GetType(TypeCode.Int32)
					);
				parameters[i] = indexParameter;
			}
			CliSyntheticMethodInfo constructor = new CliSyntheticMethodInfo(
				new CliMemberBasicInfo(this.basicInfo.module, CodedIndex.NullMethodDef)
				{
					declaringType = this,
					reflectedType = this
				},
				methodName,
				parameters,
				returnParameter,
				MethodAttributes.Public,
				MethodImplAttributes.InternalCall
				);
			return constructor;
		}
		private MethodInfo CreateGetMethod()
		{
			return this.CreateGetAccessorMethod("Get", this.GetElementType(), this._rank);
		}
		private MethodInfo CreateAddressMethod()
		{
			return this.CreateGetAccessorMethod("Address", this.GetElementType().MakeByRefType(), this._rank);
		}
		private MethodInfo CreateSetMethod()
		{
			CliParameterInfo[] parameters = new CliParameterInfo[this._rank + 1];
			for (int i = 0; i < this._rank; i++)
			{
				CliParameterInfo indexParameter = new CliParameterInfo(
					basicInfo,
					"num",
					ParameterAttributes.None,
					i + 1,
					Param.Int32Param,
					this.CliModule.CliAssembly.LoadContext.GetType(TypeCode.Int32)
					);
				parameters[i] = indexParameter;
			}
			CliParameterInfo valueParameter = new CliParameterInfo(
				basicInfo,
				"value",
				ParameterAttributes.None,
				this._rank + 1,
				new Param() { },
				this.GetElementType()
				);
			parameters[this._rank] = valueParameter;
			CliSyntheticMethodInfo constructor = new CliSyntheticMethodInfo(
				new CliMemberBasicInfo(this.basicInfo.module, CodedIndex.NullMethodDef)
				{
					declaringType = this,
					reflectedType = this
				},
				"Set",
				parameters,
				null,
				MethodAttributes.Public,
				MethodImplAttributes.InternalCall
				);
			return constructor;
		}
		protected override System.Reflection.MethodBase[] LoadDeclaredMethods()
		{
			return new MethodBase[] { 
				this.CreateSetMethod(),
				this.CreateAddressMethod(),
				this.CreateGetMethod(),
				this.CreateGetAccessorMethod("GetValue", typeof(object), 1),
				this.CreateGetAccessorMethod("GetValue", typeof(object), 2),
				this.CreateGetAccessorMethod("GetValue", typeof(object), 3),
				this.CreateConstructor1(),
				this.CreateConstructor2(),
			};
		}

		public override Type Reflect()
		{
			if (this._rank == 0)
				return this.GetElementType().MakeArrayType();
			else
				return this.GetElementType().MakeArrayType(this._rank);
		}
	}
}
